<?php

class Post_Model_Entry
{
	protected $_id = NULL;
	protected $_classIdName = NULL;
	protected $_hostClassIdName = NULL;
	protected $_hostId = NULL;
	protected $_parentId = 0;
	protected $_time = NULL;
	protected $_creatorId = NULL;
	protected $_slug = NULL;
	protected $_title = NULL;
	protected $_content = NULL;
	protected $_publishFlag = FALSE;
	protected $_deleteFlag = FALSE;
	protected $_spamFlag = FALSE;
	protected $_editTime = NULL;
	protected $_editorId = NULL;
	protected $_updateTime = NULL;
	protected $_updaterId = NULL;

	protected $_dbLoaded = FALSE;


    public function __construct(array $options = null)
    {
        if (is_array($options)) {
            $this->setOptions($options);
        }
    }


	public function setId($id = NULL)
	{
		$this->_id = $id;
		return $this;
	}
	public function setClassIdName($postClassIdName)
	{
		$this->_classIdName = $postClassIdName;
		return $this;
	}
	public function setHostClassIdName($objClassIdName)
	{
		$this->_hostClassIdName = $objClassIdName;
		return $this;
	}
	public function setHostId($objId)
	{
		$this->_hostId = $objId;
		return $this;
	}
	public function setParentId($postId)
	{
		$this->_parentId = intval($postId);
		return $this;
	}
	public function setTime($time)
	{
		$this->_time = $time;
		return $this;
	}
	public function setCreatorId($accountId)
	{
		$this->_creatorId = $accountId;
		return $this;
	}
	public function setSlug($slug)
	{
		$this->_slug = $slug;
		return $this;
	}
	public function setTitle($title)
	{
		$this->_title = $title;
		return $this;
	}
	public function setContent($content)
	{
		$this->_content = $content;
		return $this;
	}
	public function setPublishFlag($published = TRUE)
	{
		$this->_publishFlag = (bool)$published;
		return $this;
	}
	public function setDeleteFlag($deleted = TRUE)
	{
		$this->_deleteFlag = (bool)$deleted;
		return $this;
	}
	public function setSpamFlag($spam = TRUE)
	{
		$this->_spamFlag = (bool)$spam;
		return $this;
	}
	public function setEditTime($time)
	{
		$this->_editTime = $time;
		return $this;
	}
	public function setEditorId($accountId)
	{
		$this->_editorId = $accountId;
		return $this;
	}
	public function setUpdateTime($time)
	{
		$this->_updateTime = $time;
		return $this;
	}
	public function setUpdaterId($accountId)
	{
		$this->_updaterId = $accountId;
		return $this;
	}

	public function getId()
	{
		return $this->_id;
	}
	public function getClassIdName()
	{
		return $this->_classIdName;
	}
	public function getHostClassIdName()
	{
		return $this->_hostClassIdName;
	}
	public function getHostId()
	{
		return $this->_hostId;
	}
	public function getParentId()
	{
		return $this->_parentId;
	}
	public function getTime()
	{
		return $this->_time;
	}
	public function getCreatorId()
	{
		return $this->_creatorId;
	}
	public function getSlug()
	{
		return $this->_slug;
	}
	public function getTitle()
	{
		return $this->_title;
	}
	public function getContent()
	{
		return $this->_content;
	}
	public function getPublishFlag()
	{
		return $this->_publishFlag;
	}
	public function getDeleteFlag()
	{
		return $this->_deleteFlag;
	}
	public function getSpamFlag()
	{
		return $this->_spamFlag;
	}
	public function getEditTime()
	{
		return $this->_editTime;
	}
	public function getEditorId()
	{
		return $this->_editorId;
	}
	public function getUpdateTime()
	{
		return $this->_updateTime;
	}
	public function getUpdaterId()
	{
		return $this->_updaterId;
	}

	public function getIdName()
	{
		return $this->getId() .'/'. $this->getSlug();
	}
	public function getNicename()
	{
		return $this->getTitle();
	}

	public function isLoaded()
	{
		return (bool)$this->_dbLoaded;
	}


	public function getUrl($params=NULL)
	{
		$itemPrefix = Post_Model_ClassRegistry::getClassEntry($this->getClassIdName());
		$url = '/'. $this->getClassIdName() .'/'. $itemPrefix .'-'. base_convert($this->getId(), 10, 36);
		if (isset($params['slug'])) {
			$url .= '_'. $this->getSlug();
		}
		if (is_array($params)) {
			$urlParams = '';
			if (isset($params['action'])) {
				$urlParams .= 'post_action='. $params['action'];
			}
			if (empty($urlParams) === FALSE) {
				$url .= '?'. $urlParams;
			}
		}
		return $url;
	}

	//TODO: move this out of here!
	/**
	 * Check the access to an object associated to this object
	 */
	public function userHasObjectAccess($accountId, $objClassIdName, $objId, $accessName)
	{
		//TODO: load these from db
		$hostObj = Euso_Object::getInstance($this->_hostClassIdName, $this->_hostId);
		if ($hostObj === NULL) {
			return FALSE;
		}
		switch ($objClassIdName) {
		case 'comment':
			return $hostObj->userHasObjectAccess($accountId, $this->getClassIdName(), $this->getId(), $accessName .'-comment');
		}
		return FALSE;
	}


	public function touch($accountId, $recursive = FALSE)
	{
		if ($this->isLoaded() === FALSE) {
			return FALSE;
		}

		$table = Post_Model_DbTable_Entry::getInstance();

		$dbData['update_time'] = time() - date('Z');
		$dbData['updater_id'] = $accountId;

		$updRows = $table->update($dbData, array(
			'id= ?' => $this->getId(),
		));

		if (intval($updRows) != 1) {
			throw new Exception('Query error');
		}

		if ($recursive === TRUE) {
			$this->touchHost($accountId);
		}

		return TRUE;
	}
	public function touchHost($accountId)
	{
		$hostObj = Euso_Object::getInstance($this->_hostClassIdName, $this->_hostId);
		if ($hostObj && method_exists($hostObj, 'touch')) {
			return $hostObj->touch($accountId, TRUE);
		}
	}

	// Returns post id upon success
	public function save($touchHost = FALSE)
	{
		//TODO: authorization and edit/add mode

		$table = Post_Model_DbTable_Entry::getInstance();

		$title = $this->getTitle();
		$slug = $this->_generateSlug($title);

		$now = time() - date('Z');

		$dbData = array(
			'host_class' => $this->getHostClassIdName(),
			'host_id' => $this->getHostId(),
			'parent_id' => $this->getParentId(),
			'slug' => $slug,
			'title' => $title,
			'content' => $this->getContent(),
			'publish_flag' => $this->getPublishFlag(),
			'delete_flag' => $this->getDeleteFlag(),
			'spam_flag' => $this->getSpamFlag(),
		);

		if ($this->isLoaded() === FALSE) {
			$dbData['class'] = $this->getClassIdName();
			$dbData['time'] = $now;
			$dbData['creator_id'] = $this->getCreatorId();
			$dbData['update_time'] = $now;
			$dbData['updater_id'] = $this->getCreatorId();

			$insertId = $table->insert($dbData);

			$postId = $table->getAdapter()->lastInsertId();

			$this->setId($postId);
			$this->setTime($dbData['time']);
			$this->_dbLoaded = TRUE;

		} else {
			$dbData['edit_time'] = $now;
			$dbData['editor_id'] = $this->getEditorId();

			$updRows = $table->update($dbData, array(
				'id= ?' => $this->getId(),
			));

			if (intval($updRows) != 1) {
				throw new Exception('Query error');
			}

		}

		if ($touchHost === TRUE) {
			$this->touchHost($this->getCreatorId());
		}

		return $this->getId();
	}
	public function saveRevision($newContent)
	{
		//TODO:
		// - authorization
		// - match the time with edit
		// - parameters check
		// - DIFF

		if ($newContent === $this->getContent()) {
			return $this->getId();
		}

		$table = Post_Model_DbTable_Revision::getInstance();
/*
		$select = $table->select();
		$select
			->from($table, 'COUNT(*) AS _rowCount')
			->where('post_id= ?', $this->getId())
		;

		$dbResult = $table->fetchAll($select);

		if ($dbResult->count() !== 1) {
			throw new Exception('Query error');
		}

		$dbRow = $dbResult->getRow(0);

		if (intval($dbRow->_rowCount) > 0) {
			$content = xdiff_string_diff($this->getContent(), $newContent);
		} else { */
			$content = $this->getContent();
/*		} */

		$dbData = array(
			'post_id' => $this->getId(),
			'time' => time() - date('Z'),
			'editor_id' => $this->getEditorId(),
			'content' => $content,
			'comment' => NULL,
		);

		$insertId = $table->insert($dbData);
		if ($insertId < 1) {
			throw new Exception('Query error');
		}

		return $this->getId();
	}

	//TODO: place this to somewhere else. it's an utility.
	public function _generateSlug($phrase)
	{
		$result = strtolower($phrase);

		$result = preg_replace("/[^a-z0-9\s-]/", "", $result);
		$result = trim(preg_replace("/\s+/", " ", $result));
		$result = trim(substr($result, 0, 45));
		$result = preg_replace("/\s/", "-", $result);

		return $result;
	}


    public function __set($name, $value)
    {
        $method = 'set' . ucfirst($name);
        $this->$method($value);
    }
    public function __get($name)
    {
        $method = 'get' . ucfirst($name);
        return $this->$method();
    }

    public function setOptions(array $options)
    {
        $methods = get_class_methods($this);
        foreach ($options as $key => $value) {
            $method = 'set' . ucfirst($key);
            if (in_array($method, $methods)) {
                $this->$method($value);
            }
        }
        return $this;
    }


	protected static $_instanceList = array();


	/**
	 * Queries for a loaded instance.
	 */
	public static function findInstanceById($id = null)
	{
		if (intval($id) > 0 && isset(self::$_instanceList[$id]) === TRUE) {
			return self::$_instanceList[$id];
		}
		return NULL;
	}

	/**
	 * Queries for an instance. If the instance is not found, it will be loaded from the database.
	 */
	public static function getInstanceById($id)
	{
		$key = 'id'; //HACK
		$value = $id;

		if ($key === 'id') {
			$inst = self::findInstanceById($value);
		} else if ($key === 'idname') {
			$inst = self::findInstanceByIdName($value);
			$value = strtolower($value);
		} else {
			return NULL;
		}

		if ($inst instanceof Post_Model_Entry) {
			return $inst;
		}

		$inst = new Post_Model_Entry();

		$table = Post_Model_DbTable_Entry::getInstance();

		$select = $table->select();
		$select
			->where($key .'= ?', $value)
		;

		$dbResult = $table->fetchAll($select);

		if ($dbResult->count() !== 1) {
			return NULL;
		}

		$dbRow = $dbResult->getRow(0);
		$inst
			->setId($dbRow->id)
			->setClassIdName($dbRow->class)
			->setHostClassIdName($dbRow->host_class)
			->setHostId($dbRow->host_id)
			->setParentId($dbRow->parent_id)
			->setTime($dbRow->time)
			->setCreatorId($dbRow->creator_id)
			->setSlug($dbRow->slug)
			->setTitle($dbRow->title)
			->setContent($dbRow->content)
			->setPublishFlag($dbRow->publish_flag)
			->setDeleteFlag($dbRow->delete_flag)
			->setSpamFlag($dbRow->spam_flag)
			->setEditTime($dbRow->edit_time)
			->setEditorId($dbRow->editor_id)
			->setUpdateTime($dbRow->update_time)
			->setUpdaterId($dbRow->updater_id)
		;
		$inst->_dbLoaded = TRUE;

		self::$_instanceList[$dbRow->id] = $inst;

		return $inst;
	}

	public static function getInstances($class, $params)
	{
		if (is_array($params) === FALSE) {
			throw new Exception('Invalid parameter');
		}


		$table = Post_Model_DbTable_Entry::getInstance();


		$select = $table->select();

		if ($params['latest_updates']) {
			$select->order('update_time DESC');
		} else {
			if ($params['oldest'] === TRUE) {
				$select->order('time ASC');
			} else {
				$select->order('time DESC');
			}
		}


		if (empty($class) === FALSE) {
			$select->where('class= ?', $class);
		}

		if (empty($params['host_class']) === FALSE && intval($params['host_id']) > 0) {
			$select
				->where('host_class= ?', $params['host_class'])
				->where('host_id= ?', $params['host_id'])
			;
		}
		if (intval($params['creator_id']) > 0) {
			$select->where('creator_id= ?', $params['author_id']);
		}
		if (intval($params['parent_id']) > 0) {
			$select->where('parent_id= ?', $params['parent_id']);
		}
		//TODO: 3 way option: yes, no, ignore
		if ($params['deleted'] === TRUE) {
			$select->where('delete_flag= 1');
		} else {
			$select->where('delete_flag= 0');
		}
		//TODO: 3 way option: yes, no, ignore
		if ($params['draft'] === TRUE) {
			$select->where('publish_flag= 0');
		} else {
			$select->where('publish_flag= 1');
		}

		if (intval($params['count']) > 0) {
			if (isset($params['start'])) {
				$select->limit(intval($params['count']), intval($params['start']));
			} else {
				$select->limit(intval($params['count']));
			}
		} else {
			$select->limit(10);
		}


		$instances = array();

		$dbResult = $table->fetchAll($select);

		foreach ($dbResult as $dbRow) {
			if (isset(self::$_instanceList[$dbRow->id])) {
				$instances[] = self::$_instanceList[$dbRow->id];
			} else {
				$inst = new Post_Model_Entry();
				$inst
					->setId($dbRow->id)
					->setClassIdName($dbRow->class)
					->setHostClassIdName($dbRow->host_class)
					->setHostId($dbRow->host_id)
					->setParentId($dbRow->parent_id)
					->setTime($dbRow->time)
					->setCreatorId($dbRow->creator_id)
					->setSlug($dbRow->slug)
					->setTitle($dbRow->title)
					->setContent($dbRow->content)
					->setPublishFlag($dbRow->publish_flag)
					->setDeleteFlag($dbRow->delete_flag)
					->setSpamFlag($dbRow->spam_flag)
					->setEditTime($dbRow->edit_time)
					->setEditorId($dbRow->editor_id)
					->setUpdateTime($dbRow->update_time)
					->setUpdaterId($dbRow->updater_id)
				;
				$inst->_dbLoaded = TRUE;

				self::$_instanceList[$dbRow->id] = $inst;
				$instances[] = $inst;
			}
		}

		return $instances;
	}

	public static function countInstances($class, $params)
	{
		$table = Post_Model_DbTable_Entry::getInstance();

		$select = $table->select();
		$select
			->from($table, 'COUNT(*) AS rowCount')
			->where('class= ?', $class)
		;


		if (empty($params['host_class']) === FALSE && intval($params['host_id']) > 0) {
			$select
				->where('host_class= ?', $params['host_class'])
				->where('host_id= ?', $params['host_id'])
			;
		}
		if (intval($params['creator_id']) > 0) {
			$select->where('creator_id= ?', $params['author_id']);
		}
		if (intval($params['parent_id']) > 0) {
			$select->where('parent_id= ?', $params['parent_id']);
		}
		//TODO: 3 way option: yes, no, ignore
		if ($params['deleted'] === TRUE) {
			$select->where('delete_flag= 1');
		} else {
			$select->where('delete_flag= 0');
		}
		//TODO: 3 way option: yes, no, ignore
		if ($params['draft'] === TRUE) {
			$select->where('publish_flag= 0');
		} else {
			$select->where('publish_flag= 1');
		}


		$dbResult = $table->fetchAll($select);

		if ($dbResult->count() !== 1) {
			throw new Exception('Query error');
		}

		$dbRow = $dbResult->getRow(0);

		return intval($dbRow->rowCount);
	}
}
